Functions

Functions and function arguments

Functions are the building blocks of writing software. If a function is associated with an object and it's data, it is called a method.

Functions are defined using the keyword def.

There are two types of arguments

  • regular arguments, which must always be given when calling the function
  • keyword arguments, that have a default value that can be overriden if desired

Values are returned using the return keyword. If not return is defined, the default return value of all functions and methods is None, which is the null object in Python.


In [ ]:
def my_function(arg_one, arg_two, optional_1=6, optional_2="seven"):
    return " ".join([str(arg_one), str(arg_two), str(optional_1), str(optional_2)])

print(my_function("a", "b"))
print(my_function("a", "b", optional_2="eight"))

#go ahead and try out different components

Python has special syntax for catching an arbitary number of parameters. For regular parameters it is a variable with one asterisk * and for keyword parameters it is a variable with two asterisks. It is conventional to name these *args and **kwargs, but this is not required.


In [ ]:
def count_args(*args, **kwargs):
    print("i was called with " + str(len(args)) + " arguments and " + str(len(kwargs)) + " keyword arguments")
    
count_args(1, 2, 3, 4, 5, foo=1, bar=2)

The length of sequences can be checked using the built-in len() function.

It is standard practice to document a function using docstrings. A docstring is just a simple triple-quoted string immediately after the function definition. It is also possible to have docstrings in the beginning of a source code file and after a class definition.


In [ ]:
def random():
    """
    Always the number 4. 
    Chosen by fair dice roll. Guaranteed to be random.
    """
    return 4

Functions as parameters

Functions are first-class citizens in Python, which means that they can be e.g. passed to other functions. This is the first step into the world of functional programming, an elegant weapon for a more civilized age.


In [ ]:
def print_dashes():
    print("---")
    
def print_asterisks():
    print("***")
    
def pretty_print(string, function):
    function()
    print(string)
    function()
    
pretty_print("hello", print_dashes)
pretty_print("hey", print_asterisks)

Extra: Lambda

When we use the keyword def we are making a named function. Sometimes we want a simple function to use once without without binding it to any name.

Consider the following data structure.


In [ ]:
dictionaries = [
    {"name": "Jack", "age": 35, "telephone": "555-1234"},
    {"name": "Jane", "age": 40, "telephone":  "555-3331"},
    {"name": "Joe", "age": 20, "telephone": "555-8765"}
]

Now if we want to sort it using Python's built-in sort() function the sort won't know which attribute to base the sorting on.

Fortunately the sort() function takes a named parameter called key which is a function to be called on each item in the list. The return value will be used for the name.

(Python's sort() sorts the list in-place. If you want to keep the list unmodified use sorted())


In [ ]:
def get_age(x):
    return x["age"]

dictionaries.sort(key=get_age)
dictionaries

This is all nice and well, but now you have a function called get_age that you don't intend to use a second time.

An alternative way to give this would be using a lambda expression.


In [ ]:
dictionaries.sort(key=lambda x: x["age"], reverse=True)
dictionaries

The keyword lambda is followed by one or more names of parameters, in this case just the one x. The colon separates the the function definition from the function and a return is implied.

The lambda expression and the get_age function do the exact same thing.